package graph.parts;

import static ogl.vecmathimp.FactoryDefault.vecmath;
import static org.lwjgl.opengl.GL11.glDrawArrays;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import static org.lwjgl.opengl.GL20.glUseProgram;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import graph.Main;
import graph.Part;

import java.nio.FloatBuffer;

import javax.vecmath.Vector2d;

import ogl.app.Texture;
import ogl.app.TextureSkybox;
import ogl.app.Util;
import ogl.vecmath.Color;
import ogl.vecmath.Vector;
import ogl.vecmathimp.MatrixImp;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;

import shader.Shader;
import additional.Vertex;

abstract public class Shape extends Part {

	private MatrixImp matrix;

	protected FaceForm ff = FaceForm.QUADS;

	// The attribute indices for the vertex data.
	protected static int vertexAttribIdx = 0;
	protected static int colorAttribIdx = 1;
	protected static int normalAttribIdx = 2;
	protected static int textureAttribIdx = 3;

	protected FloatBuffer positionData;
	protected FloatBuffer colorData;
	protected FloatBuffer normalData;
	protected FloatBuffer textureData;

	protected Vector[] p;
	protected Color[] c;
	protected Vector[] n;
	protected Vector2d[] t;
	protected Vertex[] vertices;

	private Shader shader;

	public enum FaceForm {
		QUADS, TRIANGLES
	}

	public void initVertexData() {
		// Prepare the vertex data arrays.
		// Compile vertex data into a Java Buffer data structures that can be
		// passed to the OpenGL API efficiently.
		positionData = BufferUtils.createFloatBuffer(vertices.length
				* vecmath.vectorSize());
		colorData = BufferUtils.createFloatBuffer(vertices.length
				* vecmath.colorSize());
		normalData = BufferUtils.createFloatBuffer(vertices.length
				* vecmath.vectorSize());
		textureData = BufferUtils.createFloatBuffer(vertices.length
				* vecmath.vectorSize());

		for (Vertex v : vertices) {
			Vector2d texture = v.getTexture();
			float[] texArray = new float[2];
			texArray[0] = (float) texture.x;
			texArray[1] = (float) texture.y;
			positionData.put(v.getPosition().asArray());
			colorData.put(v.getColor().asArray());
			normalData.put(v.getNormal().asArray());
			textureData.put(texArray);
		}
		positionData.rewind();
		colorData.rewind();
		normalData.rewind();
		textureData.rewind();
	}

	@Override
	public void draw(MatrixImp m) {
		// The modeling transformation. Object to world
		shader.setModelMatrixUniform(matrix.mult(m));
//		 System.out.println(GL11.glGetError());

		// Enable the vertex data arrays (with indices 0 and 1). We use a vertex
		// position, a vertex color, a vertex normal and a vertex texture.
		glVertexAttribPointer(vertexAttribIdx, 3, false, 0, positionData);
		glEnableVertexAttribArray(vertexAttribIdx);
		glVertexAttribPointer(colorAttribIdx, 3, false, 0, colorData);
		glEnableVertexAttribArray(colorAttribIdx);
		glVertexAttribPointer(normalAttribIdx, 3, false, 0, normalData);
		glEnableVertexAttribArray(normalAttribIdx);
		glVertexAttribPointer(textureAttribIdx, 2, false, 0, textureData);
		glEnableVertexAttribArray(textureAttribIdx);

		// Draw the triangles that form the cube from the vertex data arrays.
		if (ff == FaceForm.QUADS) {
			glDrawArrays(GL11.GL_QUADS, 0, vertices.length);
		} else {
			glDrawArrays(GL11.GL_TRIANGLES, 0, vertices.length);
		}
	}

	/**
	 * Die Skybox muss immer in Relation zu der Kamera gezeichnet werden
	 * 
	 * @param viewMatrix
	 */
	public void drawSkybox(MatrixImp viewMatrix, TextureSkybox[] tex) {
		GL11.glDepthMask(false);
		
		int program = shader.getProgram();
		glUseProgram(program);
		shader.setProjectionMatrixUniform(Main.getProjectionMatrix());
		shader.setViewMatrixUniform(viewMatrix);
		// The modeling transformation. Object to world
		shader.setModelMatrixUniform(MatrixImp.identity);
		// System.out.println(GL11.glGetError());

		// Enable the vertex data arrays (with indices 0 and 1). We use a vertex
		// position, a vertex color, a vertex normal and a vertex texture.
		glVertexAttribPointer(vertexAttribIdx, 3, false, 0, positionData);
		glEnableVertexAttribArray(vertexAttribIdx);
		glVertexAttribPointer(colorAttribIdx, 3, false, 0, colorData);
		glEnableVertexAttribArray(colorAttribIdx);
		glVertexAttribPointer(normalAttribIdx, 3, false, 0, normalData);
		glEnableVertexAttribArray(normalAttribIdx);
		glVertexAttribPointer(textureAttribIdx, 2, false, 0, textureData);
		glEnableVertexAttribArray(textureAttribIdx);

		// Draw the triangles that form the cube from the vertex data arrays.
		for (int i = 0; i < 6; i++) {
			tex[i].bind();
			glDrawArrays(GL11.GL_QUADS, i*4, 4);
		}
		
		GL11.glDepthMask(true);
	}

	@Override
	public MatrixImp getMatrix() {
		return matrix;
	}

	@Override
	public void setMatrix(MatrixImp m) {
		matrix = m;
	}

	public void setShader(Shader s) {
		shader = s;
	}

	@Override
	public MatrixImp findCamera() {
		return null;
	}

	@Override
	public MatrixImp findLight() {
		return null;
	}
	
	public void moveBy(float x, float y, float z) {
		setMatrix((MatrixImp) getMatrix().mult(vecmath.translationMatrix(x, y, z)));
	}
	

	// Make construction of vertices easy on the eyes.
	public Vertex v(Vector p, Color c) {
		return new Vertex(p, c);
	}

	public Vertex v(Vector p, Color c, Vector2d t) {
		return new Vertex(p, c, t);
	}

	public Vertex v(Vector p, Color c, Vector n, Vector2d t) {
		return new Vertex(p, c, n, t);
	}

	// Make construction of vectors easy on the eyes.
	public Vector vec(float x, float y, float z) {
		return vecmath.vector(x, y, z);
	}

	// Make construction of colors easy on the eyes.
	public Color col(float r, float g, float b) {
		return vecmath.color(r, g, b);
	}

}
